package com.smart.speaker.util;

import io.github.jaredmdobson.concentus.OpusDecoder;
import io.github.jaredmdobson.concentus.OpusEncoder;
import io.github.jaredmdobson.concentus.OpusException;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;

public class OpusUtils {

    // 默认的帧大小
    private static final int DEFAULT_FRAME_SIZE = 960; // Opus典型帧大小

    /**
     * 解码Opus帧为PCM数据
     */
    public static byte[] opusFrameToPcm(OpusDecoder decoder, byte[] opusData) throws OpusException {
        decoder.resetState();
        short[] pcmBuffer = new short[DEFAULT_FRAME_SIZE];
        int samplesDecoded = decoder.decode(opusData, 0, opusData.length, pcmBuffer, 0,
                DEFAULT_FRAME_SIZE, false);
        // 转换为字节数组
        byte[] pcmBytes = new byte[samplesDecoded * 2];
        for (int i = 0; i < samplesDecoded; i++) {
            pcmBytes[i * 2] = (byte) (pcmBuffer[i] & 0xFF);
            pcmBytes[i * 2 + 1] = (byte) ((pcmBuffer[i] >> 8) & 0xFF);
        }
        return pcmBytes;
    }

    /**
     * 解码Opus帧为PCM数据（返回short数组）
     */
    public static short[] decodeOpusFrame(OpusDecoder decoder, byte[] opusData) throws OpusException {
        decoder.resetState();
        short[] pcmBuffer = new short[DEFAULT_FRAME_SIZE];
        decoder.decode(opusData, 0, opusData.length, pcmBuffer, 0, DEFAULT_FRAME_SIZE, false);
        return pcmBuffer;
    }

    /**
     * 将PCM数据转换为Opus格式
     */
    public static List<byte[]> convertPcmToOpus(OpusEncoder encoder, byte[] rawData, int sampleRate)
            throws OpusException {
        encoder.resetState();
        int frameDurationMs = 60;
        // 每帧样本数
        int frameSize = sampleRate * frameDurationMs / 1000;

        // 处理PCM数据
        List<byte[]> opusFrames = new ArrayList<>();
        byte[] opusBuffer = new byte[1275]; // 最大Opus帧大小

        for (int i = 0; i < rawData.length; i += frameSize * 2) { // 16bit=2bytes/sample
            // Get current frame
            int remaining = rawData.length - i;
            int chunkSize = Math.min(frameSize * 2, remaining);
            byte[] chunk = new byte[frameSize * 2];
            // Copy data (padding with zeros if needed)
            System.arraycopy(rawData, i, chunk, 0, chunkSize);

            // Convert to short array (16-bit samples)
            short[] pcmData = new short[frameSize];
            ByteBuffer.wrap(chunk)
                    .order(ByteOrder.LITTLE_ENDIAN)
                    .asShortBuffer()
                    .get(pcmData);

            // Encode Opus data
            int encodedBytes = encoder.encode(pcmData, 0, frameSize, opusBuffer, 0, opusBuffer.length);

            // Copy only the actual encoded bytes
            byte[] finalOpusData = new byte[encodedBytes];
            System.arraycopy(opusBuffer, 0, finalOpusData, 0, encodedBytes);
            opusFrames.add(finalOpusData);
        }

        return opusFrames;
    }
}
